﻿using System;
using System.ComponentModel;
using Loki.Utils;

namespace Loki.Events
{
    internal class WeakNotifyChangedManager : IDisposable
    {
        private WeakDictionary<INotifyPropertyChanged, WeakNotifyPropertyChangeBridge> _SourceToBridgeTable;

        public WeakNotifyChangedManager()
        {
            _SourceToBridgeTable = new WeakDictionary<INotifyPropertyChanged, WeakNotifyPropertyChangeBridge>();
        }

        public void Register<TListener>(INotifyPropertyChanged P_Source, string P_PropertyName, TListener P_Listener, Action<TListener, object, PropertyChangedEventArgs> P_PropertyChangedCallback)
        {
            WeakNotifyPropertyChangeBridge L_Bridge;
            L_Bridge = GetBridgeForSource(P_Source);

            L_Bridge.AddListener(P_PropertyName, P_Listener, P_PropertyChangedCallback);
        }

        private WeakNotifyPropertyChangeBridge GetBridgeForSource(INotifyPropertyChanged P_Source)
        {
            WeakNotifyPropertyChangeBridge L_Bridge;

            if (!_SourceToBridgeTable.TryGetValue(P_Source, out L_Bridge))
            {
                L_Bridge = AddNewPropertyBridgeToTable(P_Source);
            }

            // Can happen if the GC does it's magic
            if (L_Bridge == null)
            {
                L_Bridge = AddNewPropertyBridgeToTable(P_Source);
            }

            return L_Bridge;
        }

        private WeakNotifyPropertyChangeBridge AddNewPropertyBridgeToTable(
            INotifyPropertyChanged P_Source)
        {
            WeakNotifyPropertyChangeBridge L_Bridge = new WeakNotifyPropertyChangeBridge(P_Source);
            _SourceToBridgeTable[P_Source] = L_Bridge;
            return L_Bridge;
        }

        public void Unregister(INotifyPropertyChanged P_Source, string P_PropertyName, object P_Listener)
        {
            WeakNotifyPropertyChangeBridge L_Bridge;

            if (!_SourceToBridgeTable.TryGetValue(P_Source, out L_Bridge))
            {
                return;
            }

            if (L_Bridge == null)
            {
                _SourceToBridgeTable.Remove(P_Source);
                return;
            }

            L_Bridge.RemoveListener(P_Listener, P_PropertyName);
        }

        public void UnregisterSource(INotifyPropertyChanged P_Source)
        {
            _SourceToBridgeTable.Remove(P_Source);
        }

        public void RemoveCollectedEntries()
        {
            _SourceToBridgeTable.RemoveCollectedEntries();
        }

        #region IDisposable
        /// <summary>
        /// Releases all resources used by an instance of the <see cref="WeakNotifyChangedManager" /> class.
        /// </summary>
        /// <remarks>
        /// This method calls the virtual <see cref="Dispose(bool)" /> method, passing in <strong>true</strong>, and then suppresses 
        /// finalization of the instance.
        /// </remarks>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        ~WeakNotifyChangedManager()
        {
            Dispose(false);
        }

        /// <summary>
        /// Releases the unmanaged resources used by an instance of the <see cref="WeakNotifyChangedManager" /> class and optionally releases the managed resources.
        /// </summary>
        /// <param name="P_Disposing">Set to<strong>true</strong> to release both managed and unmanaged resources; <strong>false</strong> to release only unmanaged resources.</param>
        protected virtual void Dispose(bool P_Disposing)
        {
            if (P_Disposing)
            {
                _SourceToBridgeTable.Clear();
            }
        }
        #endregion
    }
}
